fix(fleet): retire a start's capacity wait on the next attempt - #160
Merged
Conversation
… out A remote start refused for capacity reports "instance no-capacity; retrying in 120s" — true until the next attempt is issued. The attempt that finds capacity then holds one request for the whole boot and reports nothing more, so the dashboard tile, which shows the latest line as the node's current situation, went on reporting a capacity wait for minutes, beside its own refreshes reporting the node running. remote.Start already has the fix: it calls onState with StateInFlight when a fresh attempt goes out, exactly so an observer can retire the previous attempt's verdict. The fleet node passed nil for it, so only the CLI got the benefit. Wire it through, and turn it into a line the tile can show. Guard a nil progress callback while here: remote.Start writes its lines unconditionally, so a caller passing nil was relying on which paths the start happened to take. Add the elapsed time beside the in-flight verb, recomputed on each repaint. A start's lines can legitimately stand unchanged for minutes, so a moving number is what separates a tile that is waiting from one that is wedged.
outofcoffee
force-pushed
the
worktree-dashboard-start-no-capacity
branch
from
September 3, 2026 21:44
157d8fd to
d610609
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A remote node's dashboard tile no longer reports a capacity wait for the rest of a start that has since found capacity and booted.
Summary
remote.Start'sonStatecallback through inremoteNode.StartWithProgress. It reportsStateInFlightwhenever a fresh attempt goes out, retiring whatever the previous attempt said — the mechanism the dashboard needed and never received, because the fleet node passednilfor it.progresscallback inStartWithProgress, so a caller wanting no lines says so explicitly rather than depending on which paths a given start happens to take.openspec/specs/fleet-client/spec.md: a status line must describe the node's situation now, and the tile's timings must be computed as it is drawn.Implementation details
internal/remote.Startreports through two channels.progress func(string)writes a line only immediately before a wait;onState func(string)reports the state of every poll, plusStateInFlightwhen a new attempt is issued.StateInFlightexists precisely for this case — its comment reads "an in-flight attempt supersedes an earlier no-capacity report", andinternal/remote/remote_test.gocovers it.internal/fleet/remote_node.gopassednilfor it, so onlyspinloop remote startbenefited.That left the tile holding just the
progresslines, and the sequence is:instance no-capacity; retrying in 120s, which the tile storesrunning, and is drawn below the stale lineThe CLI never showed this because it passes
progress.setStateand repaints over the stale text on a 30-second heartbeat. The dashboard had neither, which is why the tile could show a capacity wait and a running instance at once.The elapsed counter is deliberately derived from the board's clock at draw time rather than baked into a line when it arrives — a start's own lines can legitimately stand unchanged for minutes, so a moving number is what separates a tile that is waiting from one that is wedged.
dashNowis a package variable so the tile's output stays byte-stable under test.Wiring
onStateup also turned a latent nil dereference into a real one:StartWithProgress(ctx, nil)was previously safe only becauseprogresswas never called on the path an existing test took. An existing test caught it as a panic, hence the guard.This is the narrow fix. It leaves the wider fragility in place — the tile still renders a transcript line as a status, nothing carries a read timestamp (so a refresh that lands late can repaint stale data), and remote nodes stay on the 60-second cadence during a start. An openspec proposal for that rebuild follows separately.